package com.peak.spring.boot.api.service;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Maps;
import com.peak.spring.boot.api.entity.TreeEntity;
import com.peak.spring.boot.api.web.TreeVo;
import com.peak.spring.boot.tools.Converts;
import com.peak.spring.boot.tools.Emptys;

public abstract class BaseTreeServiceImpl implements TreeService {

	@Override
	public <T extends TreeEntity<T>> List<TreeVo> treeView(List<T> list) {
		Map<String, TreeVo> treeMap = Maps.newLinkedHashMap();
		Collections.sort(list);
		for (T entity : list) {
			String lineNo = entity.getLineNo();
			TreeVo entityVo = convertFrom(entity);
			treeMap.put(lineNo, entityVo);
			if (lineNo.indexOf(".") != -1) {
				String parentLineNo = StringUtils.substringBeforeLast(lineNo, ".");
				TreeVo parentVo = treeMap.get(parentLineNo);
				if (parentVo == null) {
					parentVo = getTopNode(treeMap, entity, entityVo);
				} else {
					parentVo.addNode(entityVo);
				}
			}
		}
		List<TreeVo> topTreeList = treeMap.values().stream().filter(p -> (p.getLineNo().indexOf(".") == -1)).collect(Collectors.toList());
		return topTreeList;
	}

	protected abstract <T extends TreeEntity<T>> TreeVo convertFrom(T entity);

	protected <T extends TreeEntity<T>> TreeVo getTopNode(Map<String, TreeVo> treeMap, TreeEntity<T> entity, TreeVo entityVo) {
		T parent = entity.getParent();
		if (parent.getParent() == null) {
			treeMap.put(parent.getLineNo(), entityVo);
		} else {
			TreeVo parentVo = convertFrom(parent);
			parentVo.addNode(entityVo);
			return getTopNode(treeMap, parent, parentVo);
		}
		return entityVo;
	}

	@Override
	public <T extends TreeEntity<T>> List<T> treeGrid(List<T> list) {
		Map<Integer, T> map = list.stream().collect(Collectors.toMap(T::getId, p -> p));
		for (T entity : list) {
			addParent(map, entity);
		}
		return map.values().stream().collect(Collectors.toList());
	}

	private <T extends TreeEntity<T>> void addParent(Map<Integer, T> map, T entity) {
		T parent = (T) entity.getParent();
		if (parent != null) {
			map.put(parent.getId(), parent);
			addParent(map, parent);
		}
	}

	@Override
	@Transactional
	public <T extends TreeEntity<T>> List<T> refreshLineNo(List<T> list) {
		if (Emptys.isNotEmpty(list)) {
			int index = 1;
			Map<String, Integer> lineMap = Maps.newHashMap();
			for (TreeEntity<T> treeEntity : list) {
				if (treeEntity.getLevel() == 1) {
					treeEntity.setLineNo(Converts.toString(index));
					lineMap.put(Converts.toString(index), 1);
					index++;
				} else {
					TreeEntity<T> parent = treeEntity.getParent();
					Integer subIndex = lineMap.get(parent.getLineNo());
					treeEntity.setLineNo(parent.getLineNo() + "." + (subIndex++));
					lineMap.put(parent.getLineNo(), subIndex);
					lineMap.put(treeEntity.getLineNo(), 1);
				}
			}
		}
		return list;
	}

	@Override
	public String nextNo(String lineNo) {
		String newLineNo = "1";
		if (Emptys.isNotEmpty(lineNo)) {
			if (StringUtils.contains(lineNo, ".")) {
				String pre = StringUtils.substringBeforeLast(lineNo, ".");
				String suf = StringUtils.substringAfterLast(lineNo, ".");
				newLineNo = pre + "." + (Converts.toInt(suf) + 1);
			} else {
				newLineNo = Converts.toString(Converts.toInt(lineNo) + 1);
			}
		}
		return newLineNo;
	}

}
